package com.moli.iov.handle.alarm.biz;

import com.alibaba.fastjson.JSONObject;
import com.moli.iov.base.enums.AlarmAuthEnum;
import com.moli.iov.handle.alarm.biz.utils.AlarmUtils;
import com.moli.iov.handle.alarm.enums.AlarmCodeEnum;
import com.moli.iov.handle.alarm.enums.AlarmSourceEnum;
import com.moli.iov.handle.alarm.enums.AlarmStatusEnum;
import com.moli.iov.handle.alarm.enums.AlarmTypeEnum;
import com.moli.iov.handle.alarm.enums.AntiShakeEnum;
import com.moli.iov.handle.alarm.enums.OnOffTypeEnum;
import com.moli.iov.handle.alarm.model.dto.AlarmDto;
import com.moli.iov.handle.alarm.model.dto.AlarmInfoDto;
import com.moli.iov.handle.alarm.model.dto.AuthDto;
import com.moli.iov.handle.alarm.model.dto.DisarmAlarmDto;
import com.moli.iov.handle.alarm.model.dto.OnOffLineAlarmDto;
import com.moli.iov.handle.alarm.model.po.AlarmInfo;
import com.moli.iov.handle.alarm.service.IAlarmInfoRequestService;
import com.moli.iov.handle.alarm.service.IAlarmInfoService;
import com.moli.iov.handle.alarm.service.IAlarmOtherService;
import com.moli.iov.protocol.model.dto.VehicleOnLineDto;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * <p>
 * 车线 离线报警
 * </p>
 *
 * @author zql
 * @since 2019/12/10 13:42
 */
@Slf4j
@Component
public class OfflineHandler {

  @Autowired
  private IAlarmInfoRequestService alarmInfoRequestService;

  @Autowired
  private AlarmUtils alarmUtils;

  @Autowired
  private IAlarmInfoService alarmInfoService;

  @Autowired
  private IAlarmOtherService alarmOtherService;

  /**
   * 离线报警时长
   */
  @Value("${OFF_LINE_TIME}")
  private long offLineTime;

  public void handle(OnOffLineAlarmDto onOffLineAlarmDto){

    if(OnOffTypeEnum.REPORTING.getFlag() != onOffLineAlarmDto.getOnOffType() ||
        !onOffLineAlarmDto.getVehicleOnLineDto().getStatus()){
      log.info("失联车辆报警  车辆离线 vin:{}", onOffLineAlarmDto.getVin());
      return;
    }

    if(onOffLineAlarmDto.getVehicleOnLineDto().getStatus()){
      online(onOffLineAlarmDto.getVehicleOnLineDto());
    }

  }

  /**
   * 恢复在线
   * 离线报警取消
   * 离线列表移除当前vin
   * @param vehicleOnLineDto
   */
  private void online(VehicleOnLineDto vehicleOnLineDto){
    try{
      log.info("恢复在线 vehicleOnLineDto:{}", JSONObject.toJSON(vehicleOnLineDto));
      alarmInfoRequestService.disarmAlarm(
          new DisarmAlarmDto(
              vehicleOnLineDto.getVin(),
              AlarmCodeEnum.OFF_LINE.getFlag(),
              AntiShakeEnum.NO.getFlag()));
    }catch(Exception e){
      log.error("恢复在线 取消离线报警异常", e);
    }

    alarmUtils.deleteOffLineByVin(vehicleOnLineDto.getVin());
  }


  /**
   * 失联报警
   */
  public void alarm(){

    //1、查询正在报警的车辆
    Map<String, String> offlinesOfMongodb = getOfflinesOfMongodb();

    List<String> vins = alarmOtherService.listVehicleBase();
    if(vins.isEmpty()){
      log.error("查询所有在网车辆失败，返回空");
      return;
    }
    //2、查询所有车辆最后上报时间
    Map<String, Date> lastReportTimeMap = alarmOtherService.getLastReportTimeMap(vins);
    //3、计算需要报警车辆

    lastReportTimeMap.forEach((vin, time) -> {
      if(offlinesOfMongodb.containsKey(vin)){
        log.info("失联报警 当前车辆已报警 vin:{}", vin);
        return;
      }

      if(time == null){
        log.error("失联报警 失败，最后上报时间为空 vin:{}", vin);
        return;
      }

      Long now = System.currentTimeMillis();
      Long reportTime = time.getTime();
      if((now-reportTime) >= offLineTime){

        AuthDto authDto = alarmOtherService.isAuthorized(vin);
        if(authDto.getAlarmAuths().contains(AlarmAuthEnum.OFF_LINE.getFlag())){
          log.error("车辆在线、离线报警 失败 tbox未授权 或 失联报警 不监控 vin:{}", vin);
          return;
        }

        log.info("车辆离线报警 vin：{} 授权状态：{}", vin, JSONObject.toJSON(authDto));
        alarmInfoRequestService.alarm(assembleAlarmDto(vin));
      }
    });

  }

  /**
   * 组装 AlarmDto
   * @param vin
   * @return
   */
  public AlarmDto assembleAlarmDto(String vin){
    AlarmDto alarmDto = new AlarmDto(
        vin,
        AlarmCodeEnum.OFF_LINE.getFlag(),
        AlarmCodeEnum.OFF_LINE.getLevel(),
        AlarmTypeEnum.ALARM.getFlag(),
        AlarmSourceEnum.PLATFORM.getFlag(),
        "",
        AntiShakeEnum.NO.getFlag());
    alarmDto.setIsCheckAlarming(false);
    return alarmDto;
  }

  /**
   * 获取mongodb正在报警的车辆
   * @return
   */
  private Map<String, String> getOfflinesOfMongodb(){
    AlarmInfoDto alarmInfoDto = new AlarmInfoDto();
    alarmInfoDto.setAlarmCode(AlarmCodeEnum.OFF_LINE.getFlag());
    alarmInfoDto.setStatues(
        Arrays.asList(AlarmStatusEnum.ALARMING.getFlag(), AlarmStatusEnum.IGNORE.getFlag()));
    List<AlarmInfo> alarmInfos = alarmInfoService.listAlarmInfo(alarmInfoDto);

    Map<String, String> map = new HashMap<>();
    alarmInfos.forEach(alarmInfo -> map.put(alarmInfo.getVin(), alarmInfo.getVin()));
    return map;
  }
}
